package com.giveu.signature.common.document.pdf.itext;

/**********************************************************************
 * <pre>
 * FILE : PdfTextReplacer.java
 * CLASS : PdfTextReplacer
 *
 * AUTHOR : caoxu-yiyang@qq.com
 *
 * FUNCTION : TODO
 *
 *
 *======================================================================
 * CHANGE HISTORY LOG
 *----------------------------------------------------------------------
 * MOD. NO.|   DATE   |   NAME  | REASON  | CHANGE REQ.
 *----------------------------------------------------------------------
 * 		    |2016年11月8日|caoxu-yiyang@qq.com| Created |
 * DESCRIPTION:
 * </pre>
 ***********************************************************************/

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.log.Logger;
import com.itextpdf.text.log.LoggerFactory;
import com.itextpdf.text.pdf.*;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;

/**
 * 替换PDF文件某个区域内的文本
 * @user : caoxu-yiyang@qq.com
 * @date : 2016年11月8日
 */
public class PdfReplacer {
	private static final Logger logger = LoggerFactory.getLogger(PdfReplacer.class);
	
	private int fontSize;
	private Map<String, List<ReplaceRegion>> replaceRegionMap = new HashMap<String, List<ReplaceRegion>>();
	/**List每个条目为一页的影射信息*/
	private List<Map<String,List<ReplaceRegion>>> pagesReplaceRegions;
	private Map<String, Object> replaceTextMap =new HashMap<String, Object>();
	private Map<String,String> fromMap = new HashMap<>();//表单输入项
	private Set<String> fromKeys = new HashSet<>();
	private ByteArrayOutputStream output;
	private PdfReader reader;
	private PdfStamper stamper;
	private Font font;
	
	

	public Map<String, List<ReplaceRegion>> getReplaceRegionMap() {
		return replaceRegionMap;
	}

	public PdfReplacer(byte[] pdfBytes) throws DocumentException, IOException{
		init(pdfBytes);
	}
	
	public PdfReplacer(String fileName) throws IOException, DocumentException{
		FileInputStream in = null;
		try{
			in =new FileInputStream(fileName);
			byte[] pdfBytes = new byte[in.available()];
			in.read(pdfBytes);
			init(pdfBytes);
		}finally{
			in.close();
		}
	}
	
	private void init(byte[] pdfBytes) throws DocumentException, IOException{
		reader = new PdfReader(pdfBytes);
		output = new ByteArrayOutputStream();
		stamper = new PdfStamper(reader, output);
		fromKeys =  initFromKeys();
//	    canvas = stamper.getOverContent(1);
	    setFont(12);
	}
	
	private void close() throws DocumentException, IOException{
		if(reader != null){
			reader.close();
		}
		if(output != null){
			output.close();
		}
		
		output=null;
	}
	
	public void replaceText(float x, float y, float w,float h, String text){
		ReplaceRegion region = new ReplaceRegion(text); 	//用文本作为别名
		region.setH(h);
		region.setW(w);
		region.setX(x);
		region.setY(y);
		addReplaceRegion(region);
		this.replaceText(text, text);
	}
	
	public void replaceText(String name, String text){
		this.replaceTextMap.put(name, text);
	}
	
	/**
	 * 替换文本
	 * @throws IOException 
	 * @throws DocumentException 
	 * @user : caoxu-yiyang@qq.com
	 * @date : 2016年11月9日
	 */
	private void process() throws DocumentException, IOException{
		try{
			parseReplaceText();
			
			
	        for (Map<String, List<ReplaceRegion>> replaceRegionMap : pagesReplaceRegions) {
	        	if (replaceRegionMap.isEmpty()){
					continue;
				}

	        	Set<Entry<String, List<ReplaceRegion>>> entrys = replaceRegionMap.entrySet();
	        	
	        	int pageNo = entrys.iterator().next().getValue().get(0).getPageNo();
	        	PdfContentByte canvas = stamper.getOverContent(pageNo);
		        canvas.saveState();
		        for (Entry<String, List<ReplaceRegion>> entry : entrys) {
		        	List<ReplaceRegion> value = entry.getValue();
		        	for (ReplaceRegion replaceRegion : value) {
		        		canvas.setColorFill(BaseColor.WHITE);
		        		canvas.rectangle(replaceRegion.getX(),replaceRegion.getY(),replaceRegion.getW(),replaceRegion.getH());
						
					}
				}
		        canvas.fill();
		        canvas.restoreState();
		        //开始写入文本 
		        canvas.beginText(); 
		        
		        for (Entry<String, List<ReplaceRegion>> entry : entrys) {
		        	List<ReplaceRegion> value = entry.getValue();
		        	for (ReplaceRegion replaceRegion : value) {
						
		        		//设置字体
		        		canvas.setFontAndSize(font.getBaseFont(), getFontSize());  
//		        		canvas.setTextMatrix(value.getX(),value.getY()+2/*修正背景与文本的相对位置*/);  
		        		
		        		String showString = (String) replaceTextMap.get(replaceRegion.getAliasName());
		        		int lineIndex = 0;
		        		
		        		if (font.getBaseFont().getWidthPoint(showString, getFontSize()) > replaceRegion.getW()) {
		        			float stringWidth = 0.0f;
		        			StringBuffer lineShowTextBuf = new StringBuffer();
		        			for (int i = 0; i < showString.length(); i++) {
		        				char ch = showString.charAt(i);
		        				float charWidthPoint = font.getBaseFont().getWidthPoint(ch, getFontSize());
		        				stringWidth += charWidthPoint;
		        				if (stringWidth > replaceRegion.getW()) {
		        					canvas.setTextMatrix(replaceRegion.getX(),replaceRegion.getY() - (lineIndex++) * getFontSize() + 2);  
		        					canvas.showText(lineShowTextBuf.toString());
		        					lineShowTextBuf = new StringBuffer();
		        					lineShowTextBuf.append(ch);
		        					stringWidth = charWidthPoint;
		        				} else {
		        					lineShowTextBuf.append(ch);
		        				}
		        			}
		        			canvas.setTextMatrix(replaceRegion.getX(),replaceRegion.getY()- (lineIndex++) * getFontSize()  + 2);  
		        			if (lineShowTextBuf.length() > 0) {
		        				canvas.showText(lineShowTextBuf.toString());
		        			}
		        		} else {
		        			canvas.setTextMatrix(replaceRegion.getX(),replaceRegion.getY()- (lineIndex++) * getFontSize()  + 2);  
		        			canvas.showText(showString);
		        		}
		        	}
				}
		        canvas.endText();
	        }

	        if (!this.fromMap.isEmpty()){
				BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);//设置字体
//				BaseFont bf = BaseFont.createFont("/fonts/regular.ttf", BaseFont.IDENTITY_H,BaseFont.EMBEDDED);   
				ArrayList<String> keys = new ArrayList<>();
				AcroFields acroFields = stamper.getAcroFields();
				acroFields.setGenerateAppearances(true);
				Map<String, AcroFields.Item> formFields = acroFields.getFields();

				for (Entry<String,String> entry : this.fromMap.entrySet()) {
					String key = entry.getKey();
					if (formFields.containsKey(key)){
						acroFields.setFieldProperty(key, "textfont", bf, null);
						acroFields.setField(key,entry.getValue());
						keys.add(key);
					}
				}
				stamper.setFormFlattening(true);
				//stamper.setFreeTextFlattening(true);
			}


		}finally{
			if(stamper != null){
				stamper.close();
			}
		}
	}
	
	/**
	 * 未指定具体的替换位置时，系统自动查找位置
	 * @user : caoxu-yiyang@qq.com
	 * @date : 2016年11月9日
	 */
	private void parseReplaceText() {
		PdfPositionParse parse = new PdfPositionParse(reader);
		Set<Entry<String, Object>> entrys = this.replaceTextMap.entrySet();
		for (Entry<String, Object> entry : entrys) {
			if(this.replaceRegionMap.get(entry.getKey()) == null){
				parse.addFindText(entry.getKey());
			}
		}
		
		try {
//			Map<String, ReplaceRegion> parseResult = parse.parse();
			this.pagesReplaceRegions = parse.parse();
			for (Map<String, List<ReplaceRegion>> parseResult: pagesReplaceRegions) {
				Set<Entry<String, List<ReplaceRegion>>> parseEntrys = parseResult.entrySet();
				for (Entry<String, List<ReplaceRegion>> entry : parseEntrys) {
					if(entry.getValue() != null){
						this.replaceRegionMap.put(entry.getKey(), entry.getValue());
					}
				}
			}
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
		
	}

	/**
	 * 生成新的PDF文件
	 * @user : caoxu-yiyang@qq.com
	 * @date : 2016年11月9日
	 * @param fileName
	 * @throws DocumentException
	 * @throws IOException
	 */
	public void toPdf(String fileName) throws DocumentException, IOException{
		FileOutputStream fileOutputStream = null;
		try{
			process();
			fileOutputStream = new FileOutputStream(fileName);
			fileOutputStream.write(output.toByteArray());
			fileOutputStream.flush();
		}catch(IOException e){
			logger.error(e.getMessage(), e);
			throw e;
		}finally{
			if(fileOutputStream != null){
				fileOutputStream.close();
			}
			close();
		}
		logger.info("文件生成成功");
	}
	
	/**
	 * 将生成的PDF文件转换成二进制数组
	 * @user : caoxu-yiyang@qq.com
	 * @date : 2016年11月9日
	 * @return
	 * @throws DocumentException
	 * @throws IOException
	 */
	public byte[] toBytes() throws DocumentException, IOException{
		try{
			process();
			logger.info("二进制数据生成成功");
			return output.toByteArray();
		}finally{
			close();
		}
	}
	
	/**
	 * 添加替换区域
	 * @user : caoxu-yiyang@qq.com
	 * @date : 2016年11月9日
	 * @param replaceRegion
	 */
	public void addReplaceRegion(ReplaceRegion replaceRegion){
		List<ReplaceRegion> replaceRegionList = replaceRegionMap.get(replaceRegion.getAliasName());
		if(replaceRegionList == null){
			replaceRegionList = new ArrayList<>();
			replaceRegionMap.put(replaceRegion.getAliasName(), replaceRegionList);
		}
		replaceRegionList.add(replaceRegion);
		
	}
	
	/**
	 * 通过别名得到替换区域
	 * @user : caoxu-yiyang@qq.com
	 * @date : 2016年11月9日
	 * @param aliasName
	 * @return
	 */
	public List<ReplaceRegion> getReplaceRegion(String aliasName){
		return this.replaceRegionMap.get(aliasName);
	}

	public int getFontSize() {
		return fontSize;
	}

	/**
	 * 设置字体大小
	 * @user : caoxu-yiyang@qq.com
	 * @date : 2016年11月9日
	 * @param fontSize
	 * @throws DocumentException
	 * @throws IOException
	 */
	public void setFont(int fontSize) throws DocumentException, IOException{
		if(fontSize != this.fontSize){
			this.fontSize = fontSize;
			BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
//			BaseFont.createFont();
//			BaseFont bf = BaseFont.createFont("/fonts/regular.ttf", BaseFont.IDENTITY_H,BaseFont.EMBEDDED);     
		    font = new Font(bf,this.fontSize,Font.BOLD);
		}
	}
	
	public void setFont(Font font){
		if(font == null){
			throw new NullPointerException("font is null");
		}
		this.font = font;
	}
	/**
	 * 初始化表单Map 拿到所有需要填充的值
	 * */
	private Set<String> initFromKeys(){
        Set<String> fromKeys = new HashSet<>();

        AcroFields acroFields = stamper.getAcroFields();
		if(acroFields == null){
		    return fromKeys;
        }
		Map<String, AcroFields.Item> formFields = acroFields.getFields();
		for (String key : formFields.keySet()) {
			fromKeys.add(key);
		}
		return fromKeys;
	}
	/**
	 * 设置 参数区分表单域覆盖
	 *
	 */
	public void replaceTextCheckType(String key,String value){
		if (fromKeys.contains(key)){//前隆走的表单填充模式  判断是否存在需要填充的表单
			fromMap.put(key,value);
		}else{//以前的占位符替换的模式
			replaceTextMap.put(key,value);
		}
	}
	
}
